package com.me.libgdx_test;

import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;

import com.badlogic.gdx.math.Vector3;

//Glossary of what the numbers mean in player_class_info.modifiers
//0) Health incremented on level up
//1) Attack incremented on level up
//2) Mana incremented on level up

public class ClassCharacterPlayer extends AbstractClassCharacter {
	Random random = new Random(System.currentTimeMillis());
	ClassDungeonPlayerClass player_class_info;
	ClassCharacterRace player_race_info;
	private ClassLogDisplay logger;
	//These vars dont need to be referenced
	private int total_exp;
	private float mana_potion_effectiveness,health_potion_effectiveness;
	//These do, but aren't important enough to get functions
	public int health_potions, mana_potions,gold;
	//These variables are important and need reference functions
	private int piety, max_mana, current_mana, vision;
	
	//0=false, 1=conditional_true, 2=permanently_true
	private int /*first_strike,*/ending_bonus;
	
	private float double_hit, dodge;
	private float death_gaze, life_steal;
	private float phys_atk, mag_atk;
	private float phys_def, mag_def;
	private float damage_done, damage_taken;
	
	private ClassObjectSpellDungeon[] spells;
	//private ClassCharacterClass player_class;
	private ClassPlayerClassModifiers class_mods;
	private ClassObjectGodDungeon chosen_god;
	private ClassPermanentUpgrades perm_upgrades;
	private ArrayList<String> player_animations;
	//0->Health
	//1->Mana
	//2->Exp
	private CopyOnWriteArrayList<Vector3> hud_animations;
	
	ClassCharacterPlayer(
			//ClassCharacterClass player_class, 
			ClassDungeonPlayerClass player_class_info, 
			ClassCharacterRace player_race_info,
			ClassDungeonSpell[] starting_spells,
			ClassPermanentUpgrades perm_upgrades
			/*ClassPlayerClassModifiers class_mods*/){
		logger = new ClassLogDisplay("ClassAbstractCharacter");
		player_animations = new ArrayList<String>();
		hud_animations = new CopyOnWriteArrayList<Vector3>();
		this.player_race_info = player_race_info;
		this.perm_upgrades = perm_upgrades;
		//this.player_class = player_class;
		this.class_mods = player_class_info.modifiers;
		max_health = current_health = class_mods.playerAttMod.starting_increases[0] + (int)perm_upgrades.get_upgrade_value("Ritual");
		max_mana = current_mana = class_mods.playerAttMod.starting_increases[1] + (int)perm_upgrades.get_upgrade_value("Ritual");
		this.attack = class_mods.playerAttMod.starting_increases[2];
		double_hit = class_mods.playerAttMod.dblhit_rate;
		dodge = class_mods.playerAttMod.dodge_rate;
		death_gaze = get_class_mods().playerAttMod.death_gaze;
		life_steal = get_class_mods().playerAttMod.life_steal;
		vision = get_class_mods().playerAttMod.vision_range;
		ending_bonus = get_class_mods().playerAttMod.ending_score_mod;
		phys_atk = get_class_mods().playerAttMod.power_increases[0] + get_class_mods().playerAttMod.power_increases[2];
		mag_atk = get_class_mods().playerAttMod.power_increases[1] + get_class_mods().playerAttMod.power_increases[2];
		phys_def = get_class_mods().playerAttMod.defense_increases[0] + get_class_mods().playerAttMod.defense_increases[2];
		mag_def = get_class_mods().playerAttMod.defense_increases[1] + get_class_mods().playerAttMod.defense_increases[2];
		
		//Set up the mods for the item pickup
		get_class_mods().playerPickupMod.use_item();
		
		this.player_class_info = player_class_info;
		this.level = 1;
		total_exp = 0;
		gold = 0;
		piety = 0;
		//max_mana = current_mana = 10;
		health_potion_effectiveness = get_class_mods().playerPotionMod.health_potion_mod + perm_upgrades.get_upgrade_value("Medicine");
		mana_potion_effectiveness = get_class_mods().playerPotionMod.mana_potion_mod + perm_upgrades.get_upgrade_value("Medicine");
		health_potions = get_class_mods().playerPotionMod.start_health_potion;
		mana_potions = get_class_mods().playerPotionMod.start_mana_potion;
		//first_strike = 0;
		spells = new ClassObjectSpellDungeon[]{new ClassObjectSpellDungeon(),
				new ClassObjectSpellDungeon(),new ClassObjectSpellDungeon(),
				new ClassObjectSpellDungeon()};
		get_class_mods().playerSpellMod.add_starting_spell(this, starting_spells);
		chosen_god = null;
		init_score_values();
	}
	private void init_score_values(){
		damage_done = 0;
		damage_taken = 0;
	}
	public ClassObjectGodDungeon get_god(){
		return chosen_god;
	}
	public void set_god(ClassObjectGodDungeon chosen_god){
		this.chosen_god = chosen_god;
	}
	public void add_god_action(String action){
		if(chosen_god != null){
			chosen_god.action_performed(this, action);
		}
	}
	public float get_dodge(){
		return dodge_bonus + dodge;
	}
	public float get_double_hit(){
		return dbl_hit_bonus + double_hit;
	}
	public void init_mods(ClassDungeonMods[] mods){
		ClassLogDisplay logger = new ClassLogDisplay("ClassCharacterEnemy");
		logger.print("Player Mods");
		//No reason so reinvent the wheel. Just pass the mods between classes
		attack_mods = this.get_class_mods().attack_mods;
		contracted_mods = this.get_class_mods().contracted_mods;
		immune_mods = this.get_class_mods().immune_mods;
	}
	public ClassPlayerClassModifiers get_class_mods(){
		return class_mods;
	}
	public ClassObjectSpellDungeon[] get_spells(){
		return spells;
	}
	public void trash_spell(int index){
		spells[index] = new ClassObjectSpellDungeon();
		player_race_info.trash_spell(this);
	}
	public ArrayList<String> get_player_animations(){
		return player_animations;
	}
	public CopyOnWriteArrayList<Vector3> get_hud_animations(){
		return hud_animations;
	}
	public void calc_diseases(ClassCharacterEnemy enemy){
		for(int i = 0; i < enemy.attack_mods.size();i++){
			//If the enemy has a disease, give it to the player
			//logger.print("Name: " + enemy.attack_mods.get(i).get_info_mods().Name);
			//
			ClassLogDisplay.print(enemy.attack_mods.get(i).get_info_mods().get_ailment());
			
			if(enemy.attack_mods.get(i).get_has_mod() 
					&& enemy.attack_mods.get(i).get_info_mods().get_ailment() 
					&& !immune_mods.get(i).get_has_mod()){
				logger.print("Contracted: " + this.contracted_mods.get(i).get_info_mods().Name);
				this.add_god_action("ailments");
				contracted_mods.get(i).set_has_mod(true);
			}
			//Need to put the banish stuff here...
			if(enemy.attack_mods.get(i).get_has_mod() 
					&& enemy.attack_mods.get(i).get_info_mods().Name == "Banish" 
					&& !immune_mods.get(i).get_has_mod()){
				logger.print("Contracted: " + this.contracted_mods.get(i).get_info_mods().Name);
				if(get_contracted_mod("Chains")){
					set_contracted_mod("Chains",false);
				}else{
					set_contracted_mod("Banish",true);
					//contracted_mods.get(i).set_has_mod(true);
				}
			}
		}
	}
	
	public int calc_who_first(ClassCharacterEnemy enemy){
		boolean player_first_strike = get_attack_mod("First Strike") || get_contracted_mod("First Strike");
		boolean enemy_first_strike = enemy.get_attack_mod("First Strike") || enemy.get_contracted_mod("First Strike");
		return player_first_strike == enemy_first_strike?0:(player_first_strike?1:-1);
	}
	
	public boolean check_for_death(){
		if(this.get_contracted_mod("Extra Life") && current_health<1){
			this.set_contracted_mod("Extra Life", false);
			current_health = 1;
		}
		if (current_health<1){
			return true;
		}
		return false;
	}
	public boolean roll(int hit, int total){
		return random.nextInt(total)<hit;
	}
	public Point fight_enemy(ClassCharacterEnemy enemy){
		float total_enemy_damage = damage_done;
		float total_player_damage = damage_taken;
		
		int health_before_attack = get_player_variable("current_health");
		int who_first = calc_who_first(enemy);
		
		logger.print("First Strike: " + who_first);
		//If enemy goes first. Calc player mod and
		attack_enemy(enemy);
		//player doesn't have first strike, or neither object does
		if(who_first < 1){
			attack_player(enemy);
			calc_diseases(enemy);
		}
		
		if (enemy.current_health < 1){
			if (enemy.lives > 1){
				enemy.lives--;
				enemy.current_health = 1;
			}else{
				add_enemy_exp(enemy);
			}
		}
		//enemy doesn't have first strike when player does
		if(who_first > 0 && enemy.current_health > 0){
			attack_player(enemy);
			calc_diseases(enemy);
		}
		this.set_contracted_mod("First Strike",false);
		this.reset_attack_bonus();
		this.get_class_mods().playerAttackMod.get_attack_mod_end(this, enemy, health_before_attack);
		return new Point((int)(damage_taken - total_player_damage),(int)(damage_done - total_enemy_damage));
	}
	
	public String get_player_predict_outcome(ClassCharacterEnemy enemy){
		//first strike
		//extra life
		//blind
		int player_after_attack = predict_attack_player(enemy);
		int enemy_after_attack = predict_attack_enemy(enemy);
		boolean player_first_strike = get_attack_mod("First Strike") || get_contracted_mod("First Strike");
		boolean both_first_strike = enemy.get_attack_mod("First Strike") && get_attack_mod("First Strike");
		boolean extra_life = get_contracted_mod("Extra Life");
		boolean blind = get_contracted_mod("Blind");
		boolean enemy_extra_lives = enemy.lives > 1;
		
		logger.print("Predict Stuff");
		logger.print(player_after_attack);
		logger.print(enemy_after_attack);
		logger.print(player_first_strike);
		logger.print(both_first_strike);
		logger.print(extra_life);
		logger.print(blind);
		logger.print(enemy_extra_lives);
		
		//Victory => Player isn't going to die and enemy dies
		//player must not have blind for any of these
		//player health > 0 and enemy health < 0
		//player health < 0 and enemy health < 0 and player extra lives = true and enemy extra lives = false
		//player health < 0 and enemy health < 0 and player first strike = true and enemy first strike = false and enemy extra lives = false
		//Safe => Player isn't going to die and enemy isn't going to die
		//player health > 0 and enemy health > 0
		//player health > 0 and enemy health < 0 and enemy has extra life
		//player health < 0 and player has extra life
		
		if(	!blind &&
			((enemy_after_attack < 1 && player_after_attack > 0 && !enemy_extra_lives)||
			(enemy_after_attack < 1 && player_after_attack < 1 && extra_life && !enemy_extra_lives)||
			(enemy_after_attack < 1 && player_after_attack < 1 && player_first_strike && !both_first_strike && !enemy_extra_lives))
			){
			return "Victory";
		}else if(
			(player_after_attack > 0)	||
			(player_after_attack < 1 && extra_life)
			){
			return "Safe";
		}else if(
			enemy_after_attack < 1 && player_after_attack < 1 && !enemy_extra_lives && 
				!both_first_strike && player_first_strike && blind
			){
			return "Warning";
		}else{
			return "Death";
		}
		//return "Warning";
	}
	
	public int predict_attack_player(ClassCharacterEnemy enemy){
		float current_attack = enemy.attack;
		if(enemy.get_attack_mod("Magic Attack")){
			current_attack = (int)(current_attack*(1f-mag_def));
		}else{
			current_attack = (int)(current_attack*(1f-phys_def));
		}
		if(enemy.get_contracted_mod("Weakness")){
			current_attack *= .75;
		}
		current_attack = current_attack <= 0 ? 1: current_attack;
		return current_health - (int) current_attack;
	}
	
	public void attack_player(ClassCharacterEnemy enemy){
		if(!roll((int)(get_dodge()*100),100)){
			int new_health = predict_attack_player(enemy);
			damage_taken += current_health - new_health;
			hud_animations.add(new Vector3(0,(int)-damage_taken,10));
			current_health = new_health;
			add_god_action("got_hit");
		}else{
			add_god_action("dodged");
		}
	}
	
	public int predict_attack_enemy(ClassCharacterEnemy enemy){
		float current_attack = get_total_attack(); 
		float attack_strength = 1;
		if(enemy.get_attack_mod("Magic Attack")){
			current_attack = (current_attack*(1+mag_atk));
		}else{
			current_attack = (current_attack*(1+phys_atk));
		}
		if(enemy.get_contracted_mod("+25% Physical Resistance") || enemy.get_attack_mod("+25% Physical Resistance")){
			attack_strength-=.25f;
		}
		if(enemy.get_contracted_mod("+50% Physical Resistance") || enemy.get_attack_mod("+50% Physical Resistance")){
			attack_strength-=.50f;
		}
		if(get_contracted_mod("Weakness") || get_attack_mod("Weakness")){
			attack_strength-=.25f;
		}
		current_attack = current_attack * attack_strength;
		current_attack = current_attack < 1?1:current_attack;
		return enemy.current_health - (int)current_attack;
	}
	
	public void attack_enemy(ClassCharacterEnemy enemy){
		int current_attack = enemy.current_health - predict_attack_enemy(enemy);
		//get_attack_mod_beginning
		get_class_mods().playerAttackMod.get_attack_mod_beginning(this, enemy);
		if(roll((int)(get_double_hit()*100),100)){
			//Attack mod_middle are the things that happen in the middle of the fight
			int player_damage = this.get_class_mods().playerAttackMod.get_attack_mod_middle(this, enemy, current_attack);
			enemy.current_health -= player_damage;
			damage_done += player_damage;
			
			add_god_action("hit_twice");
			add_god_action("did_hit");
		}
		int player_damage = this.get_class_mods().playerAttackMod.get_attack_mod_middle(this, enemy, current_attack);
		enemy.current_health -= player_damage;
		damage_done += player_damage;
		add_god_action("did_hit");
	}
	
	public void add_enemy_exp(ClassCharacterEnemy enemy){
		
		if(!check_for_death()){
			add_exp(calc_exp_boost(enemy.level));
		}
	}
	
	public void add_exp(int added_exp){
		
		hud_animations.add(new Vector3(2,(int)added_exp,10));
		
		total_exp += added_exp;
		int new_level = calc_level();
		logger.print("Level");
		ClassLogDisplay.print(new_level);
		logger.print("Exp");
		ClassLogDisplay.print(total_exp);
		if (new_level != level){
			perform_level_up(new_level-level);
		}
		//level_exp = (total_exp-exp_until_now())%(level*5);
	}
	
	public int exp_until_now(){
		int total = 0;
		for(int i = 0; i < level; i++){
			total += i * 5;
		}
		return total;
	}
	
	public int calc_level(){
		int i;
		int calc_exp = total_exp;
		for(i = 0; i < 11; i++){
			calc_exp-=i*5;
			if(calc_exp<0){
				break;
			}
		}
		return i;
	}
	
	public int calc_exp_boost(int eLevel){
		int holder = eLevel - level;
    	if (holder <=0){holder = 0;}
    	switch(holder){
    		case 1:return eLevel + 2; 
    		case 2:return eLevel + 6;
    		case 3:return eLevel + 12;
    		case 4:return eLevel + 20;
    		case 5:return eLevel + 30;
    		case 6:return eLevel + 42;
    		case 7:return eLevel + 56;
    		case 8:return eLevel + 72;
    		case 9:return eLevel + 90;
    		default: return eLevel;
    	}
	}
	
	public void use_potion(boolean health_potion){
		
		if (health_potion && health_potions>0 && (current_health!=get_max_health() || this.get_contracted_mod("Weakness") 
				|| this.get_contracted_mod("Blind") || this.get_contracted_mod("Poison"))){
			this.set_contracted_mod("Blind", false);
			this.set_contracted_mod("Poison", false);
			this.set_contracted_mod("Weakness", false);
			current_health += ((float)get_max_health() * health_potion_effectiveness);
			health_potions--;
			get_class_mods().playerPotionMod.potion_use(this, true);
		}else if(mana_potions>0 && (current_mana!=max_mana || this.get_contracted_mod("Mana Burn") 
				|| this.get_contracted_mod("Amnesia") || this.get_contracted_mod("Static"))){
			this.set_contracted_mod("Mana Burn", false);
			this.set_contracted_mod("Amnesia", false);
			this.set_contracted_mod("Static", false);
			current_mana += ((float)max_mana * mana_potion_effectiveness);
			mana_potions--;
			get_class_mods().playerPotionMod.potion_use(this, false);
		}
		check_health_mana();
	}
	public void refresh_shroud(int shroud_amount, ClassPlaceObjects board_object){
		if(!check_for_death()){
			get_class_mods().playerShroudMod.set_enemy_visibility(this, board_object.get_shroud(), board_object.get_enemies());
			get_class_mods().playerShroudMod.set_see_through_shroud(this, board_object);
			get_class_mods().playerShroudMod.set_enemy_visibility(this, board_object.get_shroud(), board_object.get_enemies());
			get_class_mods().playerShroudMod.cure_disease_chance(this);
			if(this.get_contracted_mod("Blood") && !this.get_contracted_mod("Mana Burn")){
				current_mana += 2* shroud_amount * get_class_mods().playerShroudMod.restore_mana * level;
			}else if(this.get_contracted_mod("Mind") && !this.get_contracted_mod("Poison")){
				current_health += 2* shroud_amount * get_class_mods().playerShroudMod.restore_health * level;
			}else{
				if(!this.get_contracted_mod("Poison")){
					current_health += shroud_amount * get_class_mods().playerShroudMod.restore_health * level;
				}
				if(!this.get_contracted_mod("Mana Burn")){
					current_mana += shroud_amount * get_class_mods().playerShroudMod.restore_mana * level;
				}
			}
			check_health_mana();
		}
	}
	public void check_health_mana(){
		check_health();
		if(current_mana > max_mana){
			current_mana = max_mana;
		}
	}
	public float return_mana_stored(){
		return (float)current_mana/(float)max_mana;
	}
	public float return_levelup_progress(){
		return (float)(total_exp-exp_until_now())/(level*5);
	}
	
	public int return_levelup_exp(){
		return (int)((float)(total_exp-exp_until_now())%(level*5));
	}
	
	public void perform_level_up(int level_difference){
		//You have leveled up here
		current_health = get_max_health();
		int extra_increase = 0;
		if(check_for_spell("Knowledge")){
			extra_increase++;
		}
		for (int i = 0; i < level_difference; i++){
			if (level!=10){
				//Put this in the classcharacterclass
				max_health += get_class_mods().playerAttMod.levelup_increases[0] + extra_increase;
				max_mana += get_class_mods().playerAttMod.levelup_increases[1] + extra_increase;
				attack += get_class_mods().playerAttMod.levelup_increases[2] + extra_increase;
				//player_class.level_up(this);
				current_mana = max_mana;
				current_health = get_max_health();
				cure_all();
				level++;
				player_animations.add("animation_levelup");
			}
		}
	}
	
	//Put all the picked up things here
	public void pickedup(String variable){
		if (variable == "Attack Boost"){
			attack+=get_class_mods().playerPickupMod.attack_bonus;
		}
		if (variable == "Health Boost"){
			max_health+=5;
		}
		if (variable == "Health Potion"){
			health_potions++;
		}
		if (variable == "Mana Boost"){
			if(get_class_mods().playerPickupMod.mana_health){
				max_health+=5;
			}else{
				max_mana+=5;
			}
		}
		if (variable == "Mana Potion"){
			if(get_class_mods().playerPickupMod.mana_health){
				health_potions++;
			}else{
				mana_potions++;
			}
		}
		if (variable == "Gold"){
			gold+=10+perm_upgrades.get_upgrade_value("Refining");
		}
		if (variable == "Blood Pool"){
			this.get_class_mods().playerPickupMod.use_blood_pool(this);
		}
	}
	
	public int get_total_attack(){
		return (int)(get_attack_bonus()*(float)attack + attack);
	}
	
	public boolean check_for_spell(String spell_name){
		for(ClassObjectSpellDungeon spell: spells){
			if(spell.get_spell_info() != null && spell.get_spell_info().Name == spell_name){
				return true;
			}
		}
		return false;
	}
	
	public ClassScoreHolder calculateScore(ClassBoardHolder board){
		return new ClassScoreHolder(board, (int) damage_done, (int) damage_taken, gold, 
				get_class_mods().playerAttackMod.get_kills(), get_class_mods().playerAttackMod.get_deaths(), level, ending_bonus,check_for_spell("Medal"));
	}
	
	public float get_player_variable_float(String variable){
		if (variable.equals("double_hit")){return double_hit;}
		if (variable.equals("dodge")){return dodge;}
		if (variable.equals("death_gaze")){return death_gaze;}
		if (variable.equals("life_steal")){return life_steal;}
		if (variable.equals("phys_atk")){return phys_atk;}
		if (variable.equals("mag_atk")){return mag_atk;}
		if (variable.equals("phys_def")){return phys_def;}
		if (variable.equals("mag_def")){return mag_def;}
		if (variable.equals("health_potion_effectiveness")){return health_potion_effectiveness;}
		if (variable.equals("mana_potion_effectiveness")){return mana_potion_effectiveness;}
		return -1;
	}
	
	public int get_player_variable(String variable){
		//logger.print("Set: " + variable);
		if (variable.equals("piety")){return piety;}
		if (variable.equals("attack")){return attack;}
		if (variable.equals("current_mana")){return current_mana;}
		if (variable.equals("current_health")){return current_health;}
		if (variable.equals("max_mana")){return max_mana;}
		if (variable.equals("max_health")){return max_health;}
		if (variable.equals("mana_potions")){return mana_potions;}
		if (variable.equals("health_potions")){return health_potions;}
		if (variable.equals("current_exp")){return total_exp;}
		if (variable.equals("level")){return level;}
		if (variable.equals("vision")){return vision;}
		if (variable.equals("gold")){return gold;}
		/*
		if (variable.equals("first_strike")){return first_strike;}
		*/
		if (variable.equals("lives")){return get_contracted_mod("Extra Life")?1:0;}
		return -1;
	}
	
	public void set_player_variable_float(String variable, float value){
		if (variable.equals("double_hit")){double_hit = value;}
		if (variable.equals("dodge")){dodge = value;}
		if (variable.equals("death_gaze")){death_gaze = value;}
		if (variable.equals("life_steal")){life_steal = value;}
		if (variable.equals("phys_atk")){phys_atk = value;}
		if (variable.equals("mag_atk")){mag_atk = value;}
		if (variable.equals("phys_def")){phys_def = value;}
		if (variable.equals("mag_def")){mag_def = value;}
		if (variable.equals("health_potion_effectiveness")){health_potion_effectiveness = value;health_potion_effectiveness=health_potion_effectiveness>1?1:health_potion_effectiveness;}
		if (variable.equals("mana_potion_effectiveness")){mana_potion_effectiveness = value;mana_potion_effectiveness=mana_potion_effectiveness>1?1:mana_potion_effectiveness;}
	}
	
	public void set_player_variable(String variable, int value){
		logger.print("Get: " + variable + ": " + value);
		if (variable.equals("piety")){piety = value;}
		if (variable.equals("attack")){attack = value;}
		if (variable.equals("gold")){gold = value;}
		if (variable.equals("max_health")){max_health = value;}
		if (variable.equals("max_mana")){max_mana = value;}
		if (variable.equals("current_exp")){total_exp = value;add_exp(0);}
		if (variable.equals("current_health")){current_health = value;check_health_mana();}
		if (variable.equals("current_mana")){current_mana = value;check_health_mana();}
		if (variable.equals("mana_potions")){mana_potions = value;get_class_mods().playerPotionMod.potion_pickup(this, false);}
		if (variable.equals("health_potions")){health_potions = value;get_class_mods().playerPotionMod.potion_pickup(this, true);}
		//if (variable.equals("first_strike")){first_strike = value;}
		if (variable.equals("lives")){if(value>0){set_contracted_mod("Extra Life", true);}}
	}
}
